[Rust] TypedIdでidに型を割り当てる
Introduction
RustでDBなどから取得したデータをモデル化して使う場合、
下記のようにidをDBの形式に合わせて持たせることが多いです。
struct Foo { id : u32, bar : String }
これはこれでよいのですが、例えばFoo構造体のidとBar構造体のidは
同じu32型であっても別のものとして扱いたいところです。
そんなときに使えるのが、今回紹介するTypedIdです。
TypedIdは汎用的なラッパーであり、idに対して単一の型を導入できます。
TypedIdのインスタンスには、対象の構造体にそのid型を関連付ける
ためのgeneric parameterがあります。
ではTypedIdを使ってみましょう。
Environments
- MacBook Pro (13-inch, M1, 2020)
- OS : MacOS 13.5.2
- Rust : 1.75.0
Try
Setup
Cargoでプロジェクトを作成したあと、
TypedId crateを追加します。
% cargo new typedid-example % cd /path/your/typedid-example % cargo add typed_id paste
pasteはマクロ内で新しい識別子を生成するcrateで、
後述するid_type!を使うときに必要です。
実装してみる
src/main.rsでTypedIdをつかってみましょう。
下記のように、User型、Customer型を定義し、
そのidとしてUserId型、CustomerId型を使っています。
id型はTypedIdをつかって実際の型(u32)と、どの構造体の型なのかを
指定しています。
use typed_id::TypedId; #[derive(Debug)] pub struct User { id: UserId, name: String } #[derive(Debug)] pub struct Customer { id: CustomerId, name: String } pub type UserId = TypedId<u32, User>; pub type CustomerId = TypedId<u32, Customer>;
構造体のインスタンスを作成してassertで比較してみます。
UserとCustomerのidはおなじ値でも、
比較すると違うものとして扱われます。
fn main() { let id = 10; let user = User { id: id.into(), name: String::from("taro") }; let customer = Customer { id: id.into(), name: String::from("customer") }; assert_eq!(id, *user.id); assert_eq!(id, *customer.id); assert_eq!(*user.id,*customer.id); }
id_type!マクロを使えば、各Id型をtypeで定義する必要はありません。
use typed_id::id_type; pub struct Order { id: OrderId } //pub type OrderId = TypedId<u32, Order> と同じ意味 id_type!(u32, Order);